package com.atguigu.tingshu.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.MongoUtil;
import com.atguigu.tingshu.model.user.UserListenProcess;
import com.atguigu.tingshu.user.service.UserListenProcessService;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackStatMqVo;
import com.atguigu.tingshu.vo.user.LastUserListenPlayVo;
import com.atguigu.tingshu.vo.user.UserListenProcessVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserListenProcessServiceImpl implements UserListenProcessService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private KafkaService kafkaService;

    /**
     * 关于声音的播放进度应该使用什么技术进行存储？ redis、mysql、mongodb
     * 声音的播放进度数据特点：数据量较比大, 数据价值不高
     * 存储技术选择：mongodb
     * <p>
     * 使用MongoTemplate来操作mongodb
     * 定义mongodb存储声音的播放进度的数据模型
     * <p>
     * 数据模型中的属性：
     * id、albumId、trackId、breakSecond、userId、createTime、updateTime
     * <p>
     * 注意：如果把声音的播放进度全部保存到一个集合中，当该集合的数据量越来越大的时候，基于这个集合进行数据的读写效率越来越低
     * 因此为了提高数据的读写效率，那么此时就需要对数据进行分集合存储
     * <p>
     * 分集合存储的方案：
     * 1、根据用户的id进行拆分   ------> 一个用户产生了一个集合 		弊端：集合的数量太多了 ， 造成维护性较差
     * 2、固定集合数量		  ------> 假设存储声音播放进度的集合数量就是100个，每一个集合都分配一个索引，索引从0开始，一直到99 ，
     * 使用用户的id % 100进行取模计算出来集合的索引值
     */

    //更新播放进度
    @Override
    public void updateListenProcess(UserListenProcessVo userListenProcessVo) {


        Long userId = AuthContextHolder.getUserId();

        //1.根据用户id、albumId、trackId查询声音的播放进度
        Criteria criteria = Criteria.where("trackId").is(userListenProcessVo.getTrackId())
                .and("userId").is(userId).and("albumId").is(userListenProcessVo.getAlbumId());
        Query query = new Query(criteria);

        //2.进行数据查询 , 一个用户的声音播放记录存储到一个集合中
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
        //该对象表示用户收听过程的详细信息
        UserListenProcess templateOne = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);

        // 如果查询不到声音的播放进度，那么此时就新增数据
        if (templateOne == null) {
            templateOne = new UserListenProcess();
            templateOne.setBreakSecond(userListenProcessVo.getBreakSecond());
            templateOne.setUserId(userId);
            templateOne.setAlbumId(userListenProcessVo.getAlbumId());
            templateOne.setTrackId(userListenProcessVo.getTrackId());
            templateOne.setCreateTime(new Date());
            templateOne.setUpdateTime(new Date());
            mongoTemplate.insert(templateOne, collectionName);
        } else {
            // 如果可以查询到声音的播放进度，那么此时就修改数据
            templateOne.setBreakSecond(userListenProcessVo.getBreakSecond());
            templateOne.setUpdateTime(new Date());
            mongoTemplate.save(templateOne, collectionName);
        }

        //查询用户当天有没有听过这个声音
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String dateStr = simpleDateFormat.format(date);
        String redisKey = "track:play:" + userListenProcessVo.getTrackId()+ ":" + dateStr + ":" + userId;

        Boolean aBoolean = redisTemplate.hasKey(redisKey);
        if (!aBoolean){
            //不存在key，就说明是第一次进行播放
            // 向Kafka中发送更新播放量的消息
            TrackStatMqVo trackStatMqVo = new TrackStatMqVo();
            trackStatMqVo.setBusinessNo(UUID.randomUUID().toString().replaceAll("-",""));
            trackStatMqVo.setAlbumId(userListenProcessVo.getAlbumId());
            trackStatMqVo.setTrackId(userListenProcessVo.getTrackId());
            trackStatMqVo.setStatType(SystemConstant.ALBUM_STAT_PLAY);//专辑统计播放量
            trackStatMqVo.setCount(1);

            //发送消息
            kafkaService.sendMessage(KafkaConstant.QUEUE_TRACK_STAT_UPDATE, JSON.toJSONString(trackStatMqVo));
            // 向Redis中存储key
            redisTemplate.opsForValue().set(redisKey,"1",1, TimeUnit.DAYS);
        }

    }


    //获取声音的上次跳出时间
    @Override
    public BigDecimal getTrackBreakSecond(Long trackId) {

        Long userId = AuthContextHolder.getUserId();

        //构建查询条件
        Criteria criteria = Criteria.where("trackId").is(trackId)
                .and("userId").is(userId);
        Query query = new Query(criteria);
        //获取集合名称
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);

        //获取到数据
        UserListenProcess templateOne = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
        if (templateOne != null) {
            return templateOne.getBreakSecond();
        } else {
            return new BigDecimal("0.0");
        }
    }
    //获取最近一次播放声音
    @Override
    public LastUserListenPlayVo getLatelyTrack() {

        Long userId = AuthContextHolder.getUserId();

        Criteria criteria = Criteria.where("userId").is(userId);
        //查询条件
        Query query = new Query(criteria).with(Sort.by(Sort.Direction.DESC,"updateTime")).limit(1);

        //根据userId查询到集合
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
        UserListenProcess mongoTemplateOne = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
        LastUserListenPlayVo lastUserListenPlayVo = new LastUserListenPlayVo();
        if (mongoTemplateOne != null){
            lastUserListenPlayVo.setAlbumId(mongoTemplateOne.getAlbumId());
            lastUserListenPlayVo.setTrackId(mongoTemplateOne.getTrackId());
        }

        return lastUserListenPlayVo;
    }
}
